package pers.mor.tomcat.watcher;

import cn.hutool.core.io.watch.WatchMonitor;
import cn.hutool.core.io.watch.WatchUtil;
import cn.hutool.core.io.watch.Watcher;
import cn.hutool.log.LogFactory;
import pers.mor.tomcat.catalina.Context;

import java.nio.file.Path;
import java.nio.file.WatchEvent;

public class ContextFileChangeWatcher {

    private WatchMonitor monitor;
    private boolean stop = false;
    private Context context;

    public ContextFileChangeWatcher(Context context) {
        this.context = context;
        // WatchUtil 是 WatchMonitor 的工具类，提供一系列的static方法快捷使用
        // createAll 方法中的数：路径，监听深度(0或1代表监听当前目录，越大监听越深)，Watcher接口执行具体事件的实现类
        // createAll(String path， int maxDepth, Watcher watcher)
        this.monitor = WatchUtil.createAll(this.context.getBase(), Integer.MAX_VALUE, new Watching());
        this.monitor.setDaemon(true);
    }

    public void start() {
        // WatchMonitor 继承 WatchServer 继承 Thread，所以它本身就是一个线程。
        // 底层使用的是 java.nio.file.WatchEvent 的监听技术。
        this.monitor.start();
    }

    public void stop() {
        this.monitor.close();
    }

    protected class Watching implements Watcher {

        private void execute(WatchEvent<?> event) {

            Context context1 = ContextFileChangeWatcher.this.context;

            synchronized (ContextFileChangeWatcher.class) {
                String fileName = event.context().toString();
                if (ContextFileChangeWatcher.this.stop == true) {
                    return;
                }
                if (fileName.endsWith(".jar") || fileName.endsWith(".class") || fileName.endsWith(".xml")) {
                    ContextFileChangeWatcher.this.stop = true;
                    try {
                        // 一系列文件的重新编译更新保存是需要时间，这里延迟一段时间再执行。
                        Thread.sleep(3500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    LogFactory.get().info("热部署启动：" + ContextFileChangeWatcher.this);
                    LogFactory.get().info("发现 {} Web应用下的 {} 文件被更新" , context1.getBase(), fileName);
                    context1.reload();
                }
            }
        }

        @Override
        public void onCreate(WatchEvent<?> watchEvent, Path path) {
            this.execute(watchEvent);
        }

        @Override
        public void onModify(WatchEvent<?> watchEvent, Path path) {
            this.execute(watchEvent);
        }

        @Override
        public void onDelete(WatchEvent<?> watchEvent, Path path) {
            this.execute(watchEvent);
        }

        @Override
        public void onOverflow(WatchEvent<?> watchEvent, Path path) {
            this.execute(watchEvent);
        }
    }
}
